先前我們建立的StockAPI在解析token字串時,並沒有針對解析錯誤的情況去做錯誤處理,所以如果簽證過期的話就會出現
在錯誤處理方面,Spring Boot上面有提供API的錯誤處理,
還記得我們之前實作JWTCheckFilter的範例嗎?
由於我們此次的錯誤是從filter拋出的,因此只透過@RestControllerAdvice
和@ExceptionHandler是捕捉不到錯誤的,原因是ControllerAdvice只能捕捉到從有@Controller這個註解類拋出的錯誤,而從更外層的filter產生的錯誤以下提供兩種思路去捕捉錯誤
1.透過實作ErrorController(就是會出現white label頁的控制類),使filter拋出的錯誤重新回到
@Controller控制類的底下,ControllerAdvice就可以捕捉
2.在filter用try-catch捕捉錯誤後直接寫出結果
在這邊我們實作第一種方法
先實作ErrorController類
package com.stockAPI.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class TokenErrorControllerImpl implements ErrorController {
@RequestMapping("/error")
public void handleError(HttpServletRequest request) throws Throwable {
//只要遇到exception就直接拋出
if (request.getAttribute("javax.servlet.error.exception") != null) {
throw (Throwable) request.getAttribute("javax.servlet.error.exception");
}
}
}
@RestControllerAdvice -BaseHandler
package com.stockAPI.exceptionHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.stockAPI.enumsave.TokenEnum;
import com.stockAPI.exception.TokenException;
import com.stockAPI.model.APIReturnObject;
import io.jsonwebtoken.ExpiredJwtException;
@RestControllerAdvice
public class BaseHandler {
static Logger logger = LogManager.getLogger();
// 登入驗證錯誤
@ExceptionHandler(UsernameNotFoundException.class)
public ResponseEntity<APIReturnObject> usernameNotFoundException(UsernameNotFoundException e) {
APIReturnObject aPIReturnObject = new APIReturnObject();
aPIReturnObject.setMessage(e.getMessage());
return new ResponseEntity<APIReturnObject>(aPIReturnObject, HttpStatus.FORBIDDEN);
}
// token過期
@ExceptionHandler(ExpiredJwtException.class)
public ResponseEntity<APIReturnObject> expiredJwtException(ExpiredJwtException e) {
APIReturnObject aPIReturnObject = new APIReturnObject();
aPIReturnObject.setMessage(TokenEnum.TOKEN_ERROR_EXPIRED.getMessage());
return new ResponseEntity<APIReturnObject>(aPIReturnObject, HttpStatus.REQUEST_TIMEOUT);
}
// 身分驗證有誤
@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<APIReturnObject> AuthenticationException(AuthenticationException e) {
logger.error(e.getMessage());
APIReturnObject aPIReturnObject = new APIReturnObject();
aPIReturnObject.setMessage(TokenEnum.TOKEN_AUTH_FAIL.getMessage());
return new ResponseEntity<APIReturnObject> (aPIReturnObject,TokenEnum.TOKEN_AUTH_FAIL.getHttpstatus());
}
}
TokenEnum-儲存回傳訊息和狀態碼
package com.stockAPI.enumsave;
import org.springframework.http.HttpStatus;
public enum TokenEnum {
Token_SUCCESS(200,"tokem資訊取得成功",HttpStatus.OK),
TOKEN_ERROR_EXPIRED(1000,"token驗證過期",HttpStatus.REQUEST_TIMEOUT),//httpstatus 408
TOKEN_AUTH_FAIL(1001,"身分驗證發生錯誤",HttpStatus.FORBIDDEN);//httpstatus 403
private Integer code;
private String message;
private HttpStatus httpstatus;
private TokenEnum( Integer code, String message,HttpStatus httpstatus) {
this.code = code;
this.message = message;
this.httpstatus =httpstatus;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
public HttpStatus getHttpstatus() {
return httpstatus;
}
}
在傳送一次post請求就可以發現回傳的格式和狀態碼已經改變
想了解更多的例外處理和filter可以推薦看這兩篇
參考資料
How to manage exceptions thrown in filters in Spring?